<?php
namespace Kuoqi\Worker;
class WebSocket extends Socket{

    /**
     * 初始化函数信息
     * WebSocket constructor.
     * @param $address
     * @param $port
     */
    public function __construct($server_addr,$server_port)
    {
        parent::__construct($server_addr,$server_port);
        //todo--这里加你想加的东西
    }

    /**
     * 关于监听的部分
     */
    public function listen(){
        $socketArr = $this->sockets;
        $write = NULL;
        $except = NULL;
        //自动选择来消息的socket 如果是握手 自动选择主机
        //int socket_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] )
        //无线阻塞
        if(false ===@socket_select($socketArr, $write, $except, 0)){
            $this->error("socket_select() failed, reason: " . socket_strerror(socket_last_error()));
        }
        foreach ($socketArr as $k=>$socket){
            $this->log('socket in');
            if ($socket == $this->master){
                //如果是关于主机的链接
                $client = socket_accept($this->master);
                //准备链接的时候已经断了，这个不管
                if ($client < 0){
                    $this->log("socket_accept() failed");
                    continue;
                }
                $this->doConnect($client);//执行链接客户端的操作
            }else{
                //如果是关于客户端的一些链接发送信息类
                $bytes = socket_recv($socket,$buffer,2048,0);
                if ($bytes == 0){
                    $this->doDisconnect($socket);//这个客户端突然发送一些为空的数据，标识自动断开了，GG
                }else{
                    //登录,$key为对应序号
                    //$key = array_search($socket, $this->sockets);
                    $this->doMessage($socket, $this->decode($buffer));
                }
            }
        }
    }

    //发送内容给客户端
    public function doSend($client, $msg){
        if(is_array($msg)){
            $msg = json_encode($msg);
        }
        $msg = $this->encode($msg);
        return @socket_write($client, $msg, strlen($msg));
    }

    //解码
    function decode($buffer) {
        $len = $masks = $data = $decoded = null;
        if(!$buffer){
            //如果发现这个不符合基础规范的话，如何处理
            return ;
        }
        $len = ord($buffer[1]) & 127;

        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        }
        else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        }
        else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    function doConnect($socket){
        $this->log('客户端链接');
        $bytes = socket_recv($socket,$buffer,2048,0);
        if($bytes==0){
            $this->log("该用户未发送任何验证数据");
        }else{
            $header = $this->getHeaders($buffer);
            list($resource, $host, $origin, $key) = $header;
            $reskey = $this->calcKey($key);
            $upgrade
                = "HTTP/1.1 101 Switching Protocol\r\n" .
                "token:XXX\r\n".
                "Upgrade: websocket\r\n" .
                "Connection: Upgrade\r\n" .
                "Sec-WebSocket-Accept: " . $reskey . "\r\n\r\n";
            array_push($this->sockets, $socket);
            socket_write($socket, $upgrade, strlen($upgrade));
            $this->log("\n" . $socket . " CONNECTED! on ".date("Y-n-d H:i:s"));
            if($this->event['connect']){
                $closer = $this->event['connect'];
                $closer($socket,$this);
            }
        }
    }

    function getHeaders($req){
        $r = $h = $o = $key = null;
        if (preg_match("/GET (.*) HTTP/"              ,$req,$match)) { $r = $match[1]; }
        if (preg_match("/Host: (.*)\r\n/"             ,$req,$match)) { $h = $match[1]; }
        if (preg_match("/Origin: (.*)\r\n/"           ,$req,$match)) { $o = $match[1]; }
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)) { $key = $match[1]; }
        return array($r, $h, $o, $key);
    }

    /**
     * 基于websocket version 13
     * @param $key
     * @return string
     * User kuoqi
     * Datetime 2022/3/24 9:31
     * Remark
     */
    function calcKey($key){
        //基于websocket version 13
        $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        return $accept;
    }

    function doDisconnect($socket){
        $this->log('客户端断开链接');
        $index = array_search($socket, $this->sockets);
        socket_close($socket);
        $this->log($socket . " DISCONNECTED!");
        if ($index >= 0){
            unset($this->sockets[$index]);
            if($this->event['close']){
                $closer = $this->event['close'];
                $closer($socket,$this);
            }
        }
    }

    /**
     * 执行接收到消息
     * @param $client
     * @param $buffer
     * User kuoqi
     * Datetime 2022/5/8 20:28
     * Remark
     */
    function doMessage($client,$buffer){
        $this->log('客户端收到消息');
        if($this->event['message']){
            $closer = $this->event['message'];
            $closer($client,$buffer,$this);
        }

        //$this->decode($buffer);
    }

    //打包给客户端
    public function encode($buffer){
        $len = strlen($buffer);
        if($len<=125)
        {
            return "\x81".chr($len).$buffer;
        }else if($len<=65535){
            return "\x81".chr(126).pack("n", $len).$buffer;
        }else{
            return "\x81".chr(127).pack("xxxxN", $len).$buffer;
        }
    }


}